
# include <stdio.h>
# include <fcntl.h>
# include <stdlib.h>
# include <string.h>
# include <strings.h>
# include <sys/epoll.h>
# include <netinet/in.h>
# include <unistd.h>
# include <sys/wait.h>
# include <sys/types.h>
# include <arpa/inet.h>
# include <sys/socket.h>
# include <errno.h>
# include <sys/stat.h>
#include <inttypes.h>
# include <unistd.h>
# include "server.h"

int LISTENFD; // 监听描述符
int EPOLLFD;
struct epoll_event event, events[EVENTMAX];



const char *getFileType(const char *name)
{
	// a.jpg a.mp4 a.html
	// 自右向左查找‘.’字符, 如不存在返回NULL
	const char* dot = strrchr(name, '.');
	if (dot == NULL)
		return "text/plain; charset=utf-8";	// 纯文本
	if (strcmp(dot, ".html") == 0 || strcmp(dot, ".htm") == 0)
		return "text/html; charset=utf-8";
	if (strcmp(dot, ".jpg") == 0 || strcmp(dot, ".jpeg") == 0)
		return "image/jpeg";
	if (strcmp(dot, ".gif") == 0)
		return "image/gif";
	if (strcmp(dot, ".png") == 0)
		return "image/png";
	if (strcmp(dot, ".css") == 0)
		return "text/css";
	if (strcmp(dot, ".au") == 0)
		return "audio/basic";
	if (strcmp(dot, ".wav") == 0)
		return "audio/wav";
	if (strcmp(dot, ".avi") == 0)
		return "video/x-msvideo";
	if (strcmp(dot, ".mov") == 0 || strcmp(dot, ".qt") == 0)
		return "video/quicktime";
	if (strcmp(dot, ".mpeg") == 0 || strcmp(dot, ".mpe") == 0)
		return "video/mpeg";
	if (strcmp(dot, ".vrml") == 0 || strcmp(dot, ".wrl") == 0)
		return "model/vrml";
	if (strcmp(dot, ".midi") == 0 || strcmp(dot, ".mid") == 0)
		return "audio/midi";
	if (strcmp(dot, ".mp3") == 0)
		return "audio/mpeg";
	if (strcmp(dot, ".ogg") == 0)
		return "application/ogg";
	if (strcmp(dot, ".pac") == 0)
		return "application/x-ns-proxy-autoconfig";

	return "text/plain; charset=utf-8";
}





// 设置文件描述符为非阻塞
void set_fd_nohong(int fd)
{
	int flag = fcntl(fd, F_GETFL, 0);
	if (flag == -1)
	{
		perror("读取文件描述符状态错误" );

	}

	flag = flag|O_NONBLOCK;

	int req = fcntl(fd, F_SETFL, flag);
	if (req == -1)
	{
		perror("设置文件描述符阻塞失败" );

	}

	
}


void create_socket_interface(int port)
{
	printf("端口:%d\n", port );
	
	LISTENFD =  socket(AF_INET, SOCK_STREAM, 0);
	if (LISTENFD == -1)
	{
		perror("创建监听描述符错误" );
	}

	// 设置文件描述符为非阻塞	
	set_fd_nohong(LISTENFD);

	// 设置端口复用
	int optval = 1;
	setsockopt(LISTENFD, SOL_SOCKET, SO_REUSEADDR, &optval, sizeof(optval));	


	// 绑定端口
	struct	sockaddr_in  server_sock;
	server_sock.sin_family = AF_INET;
	server_sock.sin_addr.s_addr = INADDR_ANY;
	server_sock.sin_port = htons(port);
	socklen_t sock_len = sizeof(server_sock);


	int bind_status = bind(LISTENFD, (struct sockaddr *)&server_sock, sock_len);
	if (bind_status == -1)
	{
		perror("绑定失败" );
	}


	// 开启监听
	listen(LISTENFD, 30);


}


// 创建epoll根节点
void create_epoll_interface()
{
	EPOLLFD = epoll_create(1);

	event.data.fd = LISTENFD;
	event.events = EPOLLIN | EPOLLET; // 设置边缘模式
	// 将监听文件描述符添加到epoll树中
	epoll_ctl(EPOLLFD, EPOLL_CTL_ADD, LISTENFD, &event);


}


void send_heard_meg(int fd, int status, const char * mthd, const char * type, long long int length)
{
	char buf[1024] = {0};
	
	sprintf(buf+strlen(buf), "http/1.1 %d %s\r\n", status, mthd );
	sprintf(buf+strlen(buf), "Content-Type: %s\r\n", type );
	sprintf(buf+strlen(buf), "Accept-Ranges: bytes\r\n" );
	sprintf(buf+strlen(buf), "Content-Length: %lld\r\n\r\n", (long long int)length );

		printf("%lld\n", length  );
	
	write(fd,buf,strlen(buf));

}

void send_file_data(const char * path, int cfd)
{

	int fd = open(path, O_RDONLY);

	char buf[1024]={0};
	//循环读文件
	while(1)
	{
		memset(buf, 0, sizeof(buf));
		int len = read(fd, buf, sizeof(buf));
		if (len > 0)
		{
			printf("发送长度: %d\n", len );
			write(cfd, buf, len);
			usleep(100);		
		}
		else
		{
			break;
			close(fd);
		}	
	}

}

void do_get_methd(char * path, int fd)
{
	struct stat file_stat;
	// 判断类型
	int res = stat(path, &file_stat);
	if(res == -1)
	{
		perror("获取文件属性失败" );
	}


	if (S_ISDIR(file_stat.st_mode))
	{
		//是目录
	}
	else if (S_ISREG(file_stat.st_mode))
	{
		// 是文件
		// 读取文件并返回
		int length = file_stat.st_size;
		printf("%d\n", length);
		send_heard_meg(fd, 200, "OK", getFileType(path), length);
		send_file_data(path, fd);
	}



	

}

void http_request(int fd)
{
	// 循环读取数据
	char tmp[1024];
	char buf[4096];
	int len, total = 0;
	while((len = recv(fd, tmp, sizeof(tmp), 0)) > 0 )
	{

		if ((total+len) < sizeof(buf))
		{
			memcpy(buf+total,tmp, len);
		}
		total += len;

	}
	
	// 读完了
	if (len == -1 && errno == EAGAIN)
	{
		// 解析
		char * file = NULL; 
		char * pt = strstr(buf, "\r\n" );
	 	// 查找字符串
		int strl = pt - buf; // 得到字符串长度
		//截断字符串
		buf[strl] = '\0' ;

		// 获取数据
		char  methd[32], repath[1024];

		sscanf(buf, "%[^ ] %[^ ]", methd, repath);
		

		if (strcmp(repath, "/") == 0)
		{	
			file = "./"; 
		}
		else
		{
			file = repath + 1;
		}


		if (strcasecmp(methd, "get" ) == 0) // 不区分大小写判断
		{

			// V处理请求
			do_get_methd(file, fd);

		}



	}
	else if(len == 0)
	{
		// 客户端断开连接
		// 先把文件描述符从epoll上摘下来在关闭
		int status = epoll_ctl(EPOLLFD, EPOLL_CTL_DEL, fd, NULL);
		if (status == -1)
		{
			perror("摘取失败" );

		}
		// close
		close(fd);
	}


}

void add_rwfd_to_epoll(int fd)
{
	struct sockaddr_in clent_sock;
	socklen_t sock_len;
	sock_len = sizeof(clent_sock);
	int seadfd = accept(fd, (struct sockaddr*)&clent_sock, &sock_len);
	if (seadfd == -1)
	{
		perror("创建通信文件描述符失败" );

	}
	// 添加通信文件描述符到epoll树
	set_fd_nohong(seadfd); // 设置文件描述符为非阻塞
						   //
	event.data.fd = seadfd;
	event.events = EPOLLIN | EPOLLET;
	epoll_ctl(EPOLLFD, EPOLL_CTL_ADD, seadfd, &event);
}

void create_epoll_wait(char * path)
{
	printf("路径:%s\n", path );
	int nfds;
	//struct epoll_event *tmp;
	while(1)
	{
		nfds = epoll_wait(EPOLLFD, events, 20, -1);
		//tmp = events;
		for (int i = 0; i < nfds; i++)
		{
			if (events[i].data.fd == LISTENFD)
			{
				// 调用accpet创建通信描述符
				add_rwfd_to_epoll(events[i].data.fd);

			}
			else
			{
				//通信
				// 读取信息提取请求行
				http_request(events[i].data.fd);


			}

		}


		







	}


}



int epoll_run(int port, char * path)
{


	
	// 系统运行主函数 
	
	// 创建监听对象
	create_socket_interface(port);

	// 创建epoll根节点
	create_epoll_interface();

	//创建epoll循环
	create_epoll_wait(path);
	

	return 0;
	
 }


